package org.budo.shiro.session.dao.redis;

import java.io.InputStream;
import java.io.Serializable;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.budo.jdbc.dao.ResultSetHandler;
import org.budo.support.javax.sql.util.ResultSetUtil;
import org.budo.support.slf4j.Slf4j;
import org.slf4j.Logger;

/**
 * 使用 SQL 操作 redis 的shiro session dao
 * 
 * @author lmw
 */
public class BudoShiroRedisSessionDao extends AbstractBudoShiroRedisSessionDao {
    private static final Logger log = Slf4j.getLogger();

    @Override
    public Collection<Session> getActiveSessions() {
        String key = this.getSessionIdPrefix() + "*";
        Object[] parameters = new Object[] { key };

        ResultSetHandler<Collection<Session>> resultSetHandler = new ResultSetHandler<Collection<Session>>() {
            public Collection<Session> handleResultSet(ResultSet resultSet) {
                List<Session> sessions = new ArrayList<Session>();
                while (ResultSetUtil.next(resultSet)) {
                    InputStream inputStream = ResultSetUtil.getBinaryStream(resultSet, 1);
                    Serializable sessionId = BudoShiroRedisSessionDao.this.deserialize(inputStream);

                    if (null == sessionId) {
                        log.error("#35 getActiveSessions, KEYS deserialize sessionId return null, inputStream=" + inputStream + ", sessionId=" + sessionId);
                        continue;
                    }

                    Session session = BudoShiroRedisSessionDao.this.doReadSession(sessionId);
                    sessions.add(session);
                }

                return sessions;
            }
        };

        return this.getBudoJdbcDao().executeQuery(KEYS_SQL, parameters, resultSetHandler);
    }

    @Override
    protected Session doReadSession(final Serializable sessionId) {
        String key = this.getSessionIdPrefix() + sessionId;
        Object[] parameters = new Object[] { key };

        ResultSetHandler<Session> resultSetHandler = new ResultSetHandler<Session>() {
            public Session handleResultSet(ResultSet resultSet) {
                while (ResultSetUtil.next(resultSet)) {
                    InputStream inputStream = ResultSetUtil.getBinaryStream(resultSet, 1);
                    Session session = (Session) BudoShiroRedisSessionDao.this.deserialize(inputStream);

                    if (null == session) {
                        log.error("#58 doReadSession, GET deserialize session return null, inputStream=" + inputStream + ", sessionId=" + sessionId + ", session=" + session);
                        continue;
                    }

                    return session;
                }

                return null;
            }
        };

        return this.getBudoJdbcDao().executeQuery(GET_SQL, parameters, resultSetHandler);
    }

    @Override
    public void update(Session session) throws UnknownSessionException {
        InputStream sessionStream = this.serialize(session);
        if (null == sessionStream) {
            log.error("#61 session serialize to null, return, session=" + session + ", sessionStream=" + sessionStream);
            return;
        }

        String key = this.getSessionIdPrefix() + session.getId();
        long expireIn = session.getTimeout() / 1000;

        this.getBudoJdbcDao().executeUpdate(SET_SQL, new Object[] { sessionStream, key });
        this.getBudoJdbcDao().executeUpdate(EXPIRE_SQL, new Object[] { expireIn, key });
    }

    @Override
    public void delete(Session session) {
        String key = this.getSessionIdPrefix() + session.getId();

        Object[] parameters = new Object[] { key };
        this.getBudoJdbcDao().executeUpdate(DEL_SQL, parameters);
    }

    @Override
    protected Serializable doCreate(Session session) {
        String sessionId = super.generateSessionId(session).toString(); // 不包含前缀

        super.assignSessionId(session, sessionId);
        this.update(session);

        return session.getId();
    }
}